<!DOCTYPE html>


<html lang="zh-CN">
  

    <head>
      <meta charset="utf-8" />
       
      <meta name="keywords" content="hexo,前端技术，前端，前端学习" />
       
      <meta name="description" content="努力小子的博客" />
      
      <meta
        name="viewport"
        content="width=device-width, initial-scale=1, maximum-scale=1"
      />
      <title> 努力小子-博客</title>
  <meta name="generator" content="hexo-theme-ayer">
      
      <link rel="shortcut icon" href="/favicon.ico" />
       
<link rel="stylesheet" href="/dist/main.css">

      <link
        rel="stylesheet"
        href="https://cdn.jsdelivr.net/gh/Shen-Yu/cdn/css/remixicon.min.css"
      />
      
<link rel="stylesheet" href="/css/custom.css">
 
      <script src="https://cdn.jsdelivr.net/npm/pace-js@1.0.2/pace.min.js"></script>
       
 

      <link
        rel="stylesheet"
        href="https://cdn.jsdelivr.net/npm/@sweetalert2/theme-bulma@5.0.1/bulma.min.css"
      />
      <script src="https://cdn.jsdelivr.net/npm/sweetalert2@11.0.19/dist/sweetalert2.min.js"></script>

      <!-- mermaid -->
      
      <style>
        .swal2-styled.swal2-confirm {
          font-size: 1.6rem;
        }
      </style>
    </head>
  </html>
</html>


<body>
  <div id="app">
    
      
    <main class="content on">
      
<section class="cover">
    
  <div class="cover-frame">
    <div class="bg-box">
      <img src="/images/cover1.jpg" alt="image frame" />
    </div>
    <div class="cover-inner text-center text-white">
      <h1><a href="/">努力小子-博客</a></h1>
      <div id="subtitle-box">
        
        <span id="subtitle"></span>
        
      </div>
      <div>
        
      </div>
    </div>
  </div>
  <div class="cover-learn-more">
    <a href="javascript:void(0)" class="anchor"><i class="ri-arrow-down-line"></i></a>
  </div>
</section>



<script src="https://cdn.jsdelivr.net/npm/typed.js@2.0.11/lib/typed.min.js"></script>


<!-- Subtitle -->

  <script>
    try {
      var typed = new Typed("#subtitle", {
        strings: ['面朝大海，春暖花开', '努力会说谎，但努力从不会白费', '愿你一生努力，一生被爱'],
        startDelay: 0,
        typeSpeed: 200,
        loop: true,
        backSpeed: 100,
        showCursor: true
      });
    } catch (err) {
      console.log(err)
    }
  </script>
  
<div id="main">
  <section class="outer">
  
  
  

<div class="notice" style="margin-top:50px">
    <i class="ri-heart-fill"></i>
    <div class="notice-content" id="broad"></div>
</div>
<script type="text/javascript">
    fetch('https://v1.hitokoto.cn')
        .then(response => response.json())
        .then(data => {
            document.getElementById("broad").innerHTML = data.hitokoto;
        })
        .catch(console.error)
</script>

<style>
    .notice {
        padding: 20px;
        border: 1px dashed #e6e6e6;
        color: #969696;
        position: relative;
        display: inline-block;
        width: 100%;
        background: #fbfbfb50;
        border-radius: 10px;
    }

    .notice i {
        float: left;
        color: #999;
        font-size: 16px;
        padding-right: 10px;
        vertical-align: middle;
        margin-top: -2px;
    }

    .notice-content {
        display: initial;
        vertical-align: middle;
    }
</style>
  
  <article class="articles">
    
    
    
    
    <article
  id="post-ssr_spa"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2022/09/26/ssr_spa/"
    >ssr和spa</a> 
</h2>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/09/26/ssr_spa/" class="article-date">
  <time datetime="2022-09-25T16:00:00.000Z" itemprop="datePublished">2022-09-26</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/ssr/">ssr</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h4 id="都是什么"><a href="#都是什么" class="headerlink" title="都是什么"></a>都是什么</h4><h5 id="SSR"><a href="#SSR" class="headerlink" title="SSR"></a>SSR</h5><p>SSR(Server-Side Rendering) 服务端渲染<br>简单理解是将组件或页面通过服务器生成html字符串，再发送到浏览器，最后将静态标记“混合”为客户端上完全交互的应用程序</p>
<h5 id="SPA"><a href="#SPA" class="headerlink" title="SPA"></a>SPA</h5><p>SPA, 即Single Page Application，按照字面意思就是单页面应用，通俗点就是整个网站由一个html页面构成</p>
<h5 id="双方的优缺点"><a href="#双方的优缺点" class="headerlink" title="双方的优缺点"></a>双方的优缺点</h5><table>
<thead>
<tr>
<th align="left">对比</th>
<th align="left">SPA</th>
<th align="left">SSR</th>
</tr>
</thead>
<tbody><tr>
<td align="left">优势</td>
<td align="left">1.页面内容在客户端渲染 <br> 2.只关注View层，与后台耦合度低，前后端分离 <br> 3.减轻后台渲染页面的压力</td>
<td align="left">1.更好的SEO，搜索引擎工具可以查看完全渲染好的页面<br> 2.更快的内容到达时间，用户能更快地看到完整渲染好地页面</td>
</tr>
<tr>
<td align="left">劣势</td>
<td align="left">1. 首屏加载缓慢 <br> 2.SEO(搜索引擎)不友好</td>
<td align="left">1.更多的服务器负载 <br> 2.涉及构建设置和部署的更多要求，需要用node.js渲染 <br>  3.一些常用的浏览器api无法使用</td>
</tr>
</tbody></table>
<h5 id="渲染对比"><a href="#渲染对比" class="headerlink" title="渲染对比"></a>渲染对比</h5><center>spa渲染<center>

<p><img src="/images/spa.png"></p>
<center>ssr渲染<center>

<p><img src="/images/ssr.png"></p>
<h5 id="为什么说SSR比SPA快"><a href="#为什么说SSR比SPA快" class="headerlink" title="为什么说SSR比SPA快"></a>为什么说SSR比SPA快</h5><p>主要体现在请求数量上，ssr只需要请求一个html文件就能展现出页面，虽然在服务器上会调取接口，但服务器之间的通信要比客户端快。<br><br>spa慢主要慢在需要请求大量的js资源，一般的首页，请求10几个js都算少，js的加载是同步的，页面逻辑可能藏在最后几个js里面，请求-等待-下载-然后解析js-调接口-展示页面<br>项目越大，快慢对比越明显。</p>
<h5 id="为什么我们有时候觉得SSR比SPA慢"><a href="#为什么我们有时候觉得SSR比SPA慢" class="headerlink" title="为什么我们有时候觉得SSR比SPA慢"></a>为什么我们有时候觉得SSR比SPA慢</h5><p>SSR要在node层调接口组装数据生成模板，如果数据接口多，数据量大，就有可能导致渲染出html慢，导致返回更慢。<br>当然也有可能是访问人数多，因为SSR要用服务器渲染，所以当并发很多时，服务器压力就有瓶颈，导致输出html也慢。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/spa/" rel="tag">spa</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/ssr/" rel="tag">ssr</a></li></ul>

    </footer>
  </div>

   
    
</article>

    
    <article
  id="post-weiqianduan"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2022/02/25/weiqianduan/"
    >微前端</a> 
</h2>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/02/25/weiqianduan/" class="article-date">
  <time datetime="2022-02-24T16:00:00.000Z" itemprop="datePublished">2022-02-25</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/%E5%89%8D%E7%AB%AF%E5%BB%BA%E8%AE%BE/">前端建设</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>微前端是一门比较热门的技术架构。<br>微服务：一个应用分解成多个服务，每个服务独自服务内部的功能。例如原来的应用有abcd四个页面，现在分解成两个服务，第一个服务有ab两个页面，第二个服务有cd两个页面，组合在一起就和原来的应用一样。</p>
<p>当其中一个服务出现故障时，并不会影响其他服务的正常运行。</p>
<p>好处：不同的服务独立运行，服务与服务之间解耦。可以把各个服务理解成各个组件，类似前端的组件化。每个服务可以独自管理，修改一个服务不影响其他服务的运行。</p>
<p>难点：不容易确认服务的边界。当业务之间关联比较复杂时，很难拆分出各个独立的服务。</p>
<p>微前端：其实就是微服务在前端的应用。</p>
<h3 id="微前端实现方案"><a href="#微前端实现方案" class="headerlink" title="微前端实现方案"></a>微前端实现方案</h3><ul>
<li>单实例：单前页面只存在一个子应用，一般可以使用qiankun(阿里的微前端框架)</li>
<li>多实例：当前页面存在多个子应用，可以使用浏览器原生隔离方案，嵌套iframe。</li>
</ul>
<p>Tips：其实单实例也可以使用iframe，老一套的微前端技术几乎都是采用嵌iframe的方案。iframe最大的特性就是提供了浏览器原生的硬隔离方案，不论是样式隔离，js隔离这类问题统统都能被完美解决。但他的最大问题也在于他的隔离性无法被突破，导致应用间上下文无法被共享，随之带来的开发体验，产品体验问题。</p>
<h3 id="前置工作"><a href="#前置工作" class="headerlink" title="前置工作"></a>前置工作</h3><h5 id="搭建环境"><a href="#搭建环境" class="headerlink" title="搭建环境"></a>搭建环境</h5><p>主应用和子应用可以用vue和react创建，任意选择。每个应用就像开发单个项目那样用脚手架工具初始化项目就行。目录如下<br><img src="/images/dajianweiqianduan.png"></p>
<h5 id="应用注册"><a href="#应用注册" class="headerlink" title="应用注册"></a>应用注册</h5><p>在有了主应用之后，需要在主应用中注册各个子应用，参数包含如下：</p>
<ul>
<li>name: 子应用名词</li>
<li>entry: 子应用的资源入口</li>
<li>container: 主应用渲染子应用的节点</li>
<li>activeRule: 在哪些路由下渲染该子应用</li>
</ul>
<p>这些信息和我们在项目中注册路由很像，enry可以看做需要渲染的组件，container可以看做路由渲染的节点，activeRule可以看做如何匹配路由的规则。</p>
<p>注册子应用代码示例如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">// src/types.ts<br>export interface IAppInfo &#123;<br>    name: string;<br>    entry: string;<br>    container: string;<br>    acticeRule: string;<br>&#125;<br><br>// src/start.ts<br>export const registerMicroApps = (appList: IAppInfo[]) =&gt; &#123;<br>    setAppList(appList)<br>&#125;<br><br>// src/appList/index.ts<br>let appList: IAppInfo[] = []<br><br>export const setAppList = (list: IAppInfo[]) =&gt; &#123;<br>    appList = list<br>&#125;<br><br>export const getAppList = () =&gt; &#123;<br>    return appList<br>&#125;<br></code></pre></td></tr></table></figure>
<p>上述实现很简单，就只需要将用户传入的 appList 保存起来即可</p>
<h5 id="路由劫持"><a href="#路由劫持" class="headerlink" title="路由劫持"></a>路由劫持</h5><p>在有了子应用列表以后，我们需要启动微前端以便渲染相应的子应用，也就是需要判断路由来渲染相应的应用。因此，我们可以先考虑如何监听路由的变化来判断渲染哪个子应用。</p>
<p>对于非SPA（单页应用）架构的项目来说，这个完全不是什么问题，因为我们只需要在启动微前端的时候判断下当前URL并渲染应用即可；但是在SPA架构下，路由变化是不会引发页面刷新的，因此我们需要一个方式知晓路由的变化，从而判断是否需要切换子应用或者什么事都不干。</p>
<p>可通过以下两种方式监听路由的变化：<br>1.hash模式，也就是URL中携带#<br>2.history模式，也就是常见的url格式</p>
<p>hash模式点击跳转会触发hashchange事件，history模式浏览器动作如点回退按钮会触发popstate事件。点击跳转会调用pushState函数向浏览器历史添加了一个状态，并且不会向服务器发起请求。</p>
<p>所以，主要涉及到两个事件：</p>
<ul>
<li>popstate</li>
<li>hashchange</li>
</ul>
<p>要去监听这两个事件，同时，调用pushState以及replaceState也会造成路由变化，但不会触发事件，因此要去重写这两个函数。</p>
<p>代码示例如下：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><code class="hljs $xslt">// src/route/index.ts<br><br>// 保存原有方法<br>const originalPuah = window.history.pushState;<br>const originalReplace = window.history.replaceState<br><br>export const hijackRoute = () =&gt; &#123;<br>    // 重写方法<br>    window.history.pushState = (...args) =&gt; &#123;<br>        // 调用原有方法<br>        originalPuah.apply(window.history, args)<br>        // URL 改变逻辑，实际就是如何处理子应用<br>        // ...<br>    &#125;;<br>    window.history.replaceState = (...args) =&gt; &#123;<br>        originalReplace.apply(window.history, args);<br>        // URL 改变逻辑，实际就是如何处理子应用<br>        // ...<br>    &#125;<br>    <br>    // 监听事件，触发URL改变逻辑<br>    window.addEventListener(&quot;hashchange&quot;, () =&gt; &#123;&#125;);<br>    window.addEventListener(&quot;popstate&quot;, () =&gt; &#123;&#125;);<br>    <br>   // 重写<br>    window.addEventListener = hijackEventListener(window.addEventListener);<br>    window.removeEventListener = hijackEventListener(window.removeEventListener);<br>&#125;<br><br>const capturedListeners: Record&lt;EventType, Function[]&gt; = &#123;<br>    hashchange: [],<br>    popstate: [],<br>&#125;<br><br>const hasListeners = (name: EventType, fn: Function) =&gt; &#123;<br>    return capturedListeners[name].filter((listener) =&gt; listener === fn).length;<br>&#125;<br>const hijackEventListener = (func: Function): any =&gt; &#123;<br>    return function (name: string, fn: Function)&#123;<br>        // 如果是以下事件，保存回调函数<br>        if(name === &#x27;hashchange&#x27; || name === &#x27;popstate&#x27;) &#123;<br>            if(!hasListeners(name, fn))&#123;<br>                capturedListeners[name].push(fn);<br>                return;<br>            &#125; else &#123;<br>                capturedListeners[name] = capturedListeners[name].filter(<br>                     (listener) =&gt; listener !== fn<br>                 );<br>            &#125;<br>        &#125;<br>        return func.apply(window, arguments);<br>    &#125;<br>&#125;<br><br>// 后续渲染子应用后使用，用于执行之前保存的回调函数<br>// 后续渲染子应用后使用，用于执行之前保存的回调函数<br>export function callCapturedListeners() &#123;<br>  if (historyEvent) &#123;<br>    Object.keys(capturedListeners).forEach((eventName) =&gt; &#123;<br>      const listeners = capturedListeners[eventName as EventType]<br>      if (listeners.length) &#123;<br>        listeners.forEach((listener) =&gt; &#123;<br>          // @ts-ignore<br>          listener.call(this, historyEvent)<br>        &#125;)<br>      &#125;<br>    &#125;)<br>    historyEvent = null<br>  &#125;<br>&#125;<br></code></pre></td></tr></table></figure>
<p>以上代码主要做了3件事：</p>
<ol>
<li>重写pushState 以及 replaceState 方法，在方法中调用原有方法后执行如何处理子应用的逻辑</li>
<li>监听 hashchange 及 popstate 事件，事件触发后执行如何处理子应用的逻辑</li>
<li>重写监听 / 移除事件函数，如果应用监听了 hashchange 及 popstate 事件就将回调函数保存起来以备后用</li>
</ol>
<h5 id="应用生命周期"><a href="#应用生命周期" class="headerlink" title="应用生命周期"></a>应用生命周期</h5><p>其实生命周期可以参考vue组件的生命周期，对于主应用来说，分为以下三个生命周期：</p>
<ol>
<li>beforeLoad: 挂载子应用前</li>
<li>mounted: 挂载子应用后</li>
<li>unmounted: 卸载子应用<br>还可以根据自己的需要添加生命周期。</li>
</ol>
<p>对于子应用来说，通常分为以下三个生命周期</p>
<ol>
<li>bootstrap：首次应用加载触发，常用于配置子应用全局信息</li>
<li>mount：应用挂载时触发，常用于渲染子应用</li>
<li>unmount：应用卸载时触发，常用于销毁子应用</li>
</ol>
<p>实现注册主应用生命周期函数， 代码示例：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><code class="hljs $xslt">// src/types.ts<br>export interface ILifeCycle &#123;<br>    beforeLoad?: LifeCycle | LifeCycle[];<br>    mounted?: LifeCycle | LifeCycle[];<br>    unmounted?: LifeCycle | LifeCycle[];<br>&#125;<br>// src/start.ts<br>// 改写下之前的<br>export const registerMicroApps = (<br>  appList: IAppInfo[],<br>  lifeCycle?: ILifeCycle<br>) =&gt; &#123;<br>  setAppList(appList);<br>  lifeCycle &amp;&amp; setLifeCycle(lifeCycle);<br>&#125;;<br><br>// src/lifeCycle/index.ts<br>let lifeCycle: ILifeCycle = &#123;&#125;;<br><br>export const setLifeCycle = (list: ILifeCycle) =&gt; &#123;<br>  lifeCycle = list;<br>&#125;;<br></code></pre></td></tr></table></figure>
<p>子应用的生命周期：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><code class="hljs $xslt">// src/enums.ts<br>// 设置子应用状态<br>export enum AppStatus &#123;<br>  NOT_LOADED = &quot;NOT_LOADED&quot;,<br>  LOADING = &quot;LOADING&quot;,<br>  LOADED = &quot;LOADED&quot;,<br>  BOOTSTRAPPING = &quot;BOOTSTRAPPING&quot;,<br>  NOT_MOUNTED = &quot;NOT_MOUNTED&quot;,<br>  MOUNTING = &quot;MOUNTING&quot;,<br>  MOUNTED = &quot;MOUNTED&quot;,<br>  UNMOUNTING = &quot;UNMOUNTING&quot;,<br>&#125;<br><br>// src/lifeCycle/index.ts<br>export const runBeforeLoad = async (app: IInternalAppInfo) =&gt; &#123;<br>  app.status = AppStatus.LOADING;<br>  await runLifeCycle(&quot;beforeLoad&quot;, app);<br><br>  app = await 加载子应用资源;<br>  app.status = AppStatus.LOADED;<br>&#125;;<br><br>export const runBoostrap = async (app: IInternalAppInfo) =&gt; &#123;<br>  if (app.status !== AppStatus.LOADED) &#123;<br>    return app;<br>  &#125;<br>  app.status = AppStatus.BOOTSTRAPPING;<br>  await app.bootstrap?.(app);<br>  app.status = AppStatus.NOT_MOUNTED;<br>&#125;;<br><br>export const runMounted = async (app: IInternalAppInfo) =&gt; &#123;<br>  app.status = AppStatus.MOUNTING;<br>  await app.mount?.(app);<br>  app.status = AppStatus.MOUNTED;<br>  await runLifeCycle(&quot;mounted&quot;, app);<br>&#125;;<br><br>export const runUnmounted = async (app: IInternalAppInfo) =&gt; &#123;<br>  app.status = AppStatus.UNMOUNTING;<br>  await app.unmount?.(app);<br>  app.status = AppStatus.NOT_MOUNTED;<br>  await runLifeCycle(&quot;unmounted&quot;, app);<br>&#125;;<br><br>const runLifeCycle = async (name: keyof ILifeCycle, app: IAppInfo) =&gt; &#123;<br>  const fn = lifeCycle[name];<br>  if (fn instanceof Array) &#123;<br>    await Promise.all(fn.map((item) =&gt; item(app)));<br>  &#125; else &#123;<br>    await fn?.(app);<br>  &#125;<br>&#125;;<br></code></pre></td></tr></table></figure>
<p>以上代码主要做了以下几件事</p>
<ul>
<li>设置子应用状态，用于逻辑判断以及优化。比如说当一个应用状态为非 NOT_LOADED 时（每个应用初始都为 NOT_LOADED 状态），下次渲染该应用时就无需重复加载资源了</li>
<li>如需要处理逻辑，比如说 beforeLoad 我们需要加载子应用资源</li>
<li>执行主 / 子应用生命周期，这里需要注意下执行顺序，可以参考父子组件的生命周期执行顺序</li>
</ul>
<p>…未完待续…</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E5%89%8D%E7%AB%AF%E5%BB%BA%E8%AE%BE/" rel="tag">前端建设</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E5%BE%AE%E5%89%8D%E7%AB%AF/" rel="tag">微前端</a></li></ul>

    </footer>
  </div>

   
    
</article>

    
    <article
  id="post-前端沙箱"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2022/02/25/%E5%89%8D%E7%AB%AF%E6%B2%99%E7%AE%B1/"
    >前端沙箱</a> 
</h2>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/02/25/%E5%89%8D%E7%AB%AF%E6%B2%99%E7%AE%B1/" class="article-date">
  <time datetime="2022-02-24T16:00:00.000Z" itemprop="datePublished">2022-02-25</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/%E5%89%8D%E7%AB%AF%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/">前端核心概念</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>沙箱，即sandbox，顾名思义，就是让你的程序跑在一个隔离的环境下，不对外界的其他程序造成影响。通过创建类似沙盒的独立作业环境，在其内部运作并不能对硬盘产生永久性的影响。</p>
<p>举个简单的栗子，其实我们的浏览器，Chrome中的每个标签页都是一个沙箱，渲染进程被沙箱隔离，网页web代码内容必须通过IPC通道才能与浏览器内核进程进行通信，通信的过程会进行安全的检查。沙箱设计的目的是为了让不可信的代码，运行在一定的环境中，从而限制这些代码访问隔离区之外的资源。</p>
<h3 id="JS中沙箱的使用场景"><a href="#JS中沙箱的使用场景" class="headerlink" title="JS中沙箱的使用场景"></a>JS中沙箱的使用场景</h3><ol>
<li>微前端：防止子应用修改window上的属性，隔离各个子应用，避免互相影响。</li>
<li>在线编辑器：相信大家都有使用过一些在线代码编辑器，而这些代码的执行，基本都会放置在沙箱中，放置对页面本身造成影响</li>
<li>vue模板中表达式计算：vue模板中表达式的计算被放在沙盒中，只能访问全局变量的一个白名单，如 Math 和 Date 。你不能够在模板表达式中试图访问用户定义的全局变量。</li>
</ol>
<h3 id="实现-使用沙箱的方式"><a href="#实现-使用沙箱的方式" class="headerlink" title="实现/使用沙箱的方式"></a>实现/使用沙箱的方式</h3><p>iframe：是一种比较传统的微前端技术解决方案，iframe提供了浏览器原生的硬隔离方案。<br>快照：就是在挂载子应用前记录下当前 window 上的所有内容，然后接下来就随便让子应用去玩了，直到卸载子应用时恢复挂载前的 window 即可。这种方案实现容易，唯一缺点就是性能慢<br>Proxy：创建一个假的 window 出来，如果用户设置值的话就设置在 fakeWindow 上，这样就不会影响全局变量了。如果用户取值的话，就判断属性是存在于 fakeWindow 上还是 window 上。<br>可参考 qiankun 的实现方式</p>
<h3 id="沙盒应该像什么"><a href="#沙盒应该像什么" class="headerlink" title="沙盒应该像什么"></a>沙盒应该像什么</h3><h4 id="虚拟化、容器化、Docker"><a href="#虚拟化、容器化、Docker" class="headerlink" title="虚拟化、容器化、Docker"></a>虚拟化、容器化、Docker</h4><p>服务端的微服务主要是通过docker技术实现虚拟化的底层支持，使服务开发者可以体会到不同环境的区别，抹平运行时的差异。</p>
<p>在docker普遍应用之前，微服务在服务端的使用主要基于虚拟机。但是虚拟机缺点很多，浪费资源就是其中一大缺点。</p>
<h3 id="沙盒应该怎么做"><a href="#沙盒应该怎么做" class="headerlink" title="沙盒应该怎么做"></a>沙盒应该怎么做</h3><h4 id="单进程与多进程"><a href="#单进程与多进程" class="headerlink" title="单进程与多进程"></a>单进程与多进程</h4><p>参考单核、操作系统进程，模拟进程切换策略。我们的沙盒实质上在让一个浏览器去跑多个“独立”的应用，那么这里对操作系统的模仿、最终趋同一定避不开。在这个角度上，和其他语言相比 JavaScript 占了一个独特的执行特点的便宜：它自身是单线程的。我怎么做实质上也都是在一个线程内。相当于我们这个操作系统从一开始就限定了单核只有一个出力气。</p>
<p>那么一个操作系统，怎么做多进程并行呢， 单进程可简单通过根路由等等规则控制，每次只激活一个，大家做 context 切换即可；多进程并行就正好可以利用 JavaScript 的特性，我可以封装每个独立的事件循环。比如 setTimeout、各种事件回调的 handler，我们在实际 function 外面先切换 context，再执行你原本希望绑定的 function。这样是线程安全的。总结下来就会是下面两条：</p>
<p>1.用路由切换封装，模拟单核单进程。<br>2.用事件循环的总体封装，模拟单核多进程。</p>
<h4 id="Context-切换"><a href="#Context-切换" class="headerlink" title="Context 切换"></a>Context 切换</h4><p>用 context 切换来模拟线程安全， 具体的意思是在每个隔离的子应用“进程”即将开始激活时，先查找当前被激活的、其他的子应用，然后为这个将要退出的应用录制“操作系统”的全现场状态，保存为它的 context。最后为即将激活的新“进程”恢复、新建它自己的 context。</p>
<p>如上面所说，我把当前状态记录为 context，保证每个子应用都适用在自己的 context 内，不影响和改变别人的 context。这个操作全部由托管了子应用的父系统统一来切换。                     </p>
<p>体现删除 key 必须要遍历两次， 才能保证每个对象都遍历到一遍。这块要强调一个点，当你拿到新旧两个对象比较，遍历其中一个的 key、到另一个里面找，只这样做是不够的，因为有可能又删掉的东西。删掉导致 key 没有了，自然也遍历不到。想体现这个删除，必须要遍历两次，新旧两个对象都遍历一遍，才能知道相比之下谁多了什么谁少了什么。</p>
<p>Context 切换的性能够好吗？ 先说说这个快照的空间性能。如果你有 N 个沙盒需要有多少切换的组合呢，是不是 context 的全文、或者任意两个沙盒之间的 context 差异都要完整储存？实际上不用。我们只需要记录差别、context 的变化，并且只记录他对 “idle”状态的差别。例如 A、B、C、D、E、F、G。我们不需要记录 A→B A→C 这样的切换，而是虚拟一个空闲状态：O，都是 A→O，B→O，只保存他们和 O 之间的差别。需要记录比较的变量数量从子应用个数的乘法变成了加法。写一个循环就可以快速比较完变化。</p>
<p>综上所述就是让每个子应用的开始和结束、互相切换，都先回到一个虚拟的“初始状态”，恢复现场，再进入被激活的沙盒状态，每次切换仅记录一个沙盒信息。</p>
<h3 id="常用沙盒方案"><a href="#常用沙盒方案" class="headerlink" title="常用沙盒方案"></a>常用沙盒方案</h3><h4 id="CSS-沙盒"><a href="#CSS-沙盒" class="headerlink" title="CSS 沙盒"></a>CSS 沙盒</h4><p>先说 CSS 沙盒。 这块 webComponent 已经做了很多发展了很多了。这里忍不住要说，web 标准里一度有一个非常吸引我、让我感觉非常有意思的内容是 scoped css——就是加个 Attribute 就能结合 DOM 树限制 CSS 作用范围。后来这个标准被取消了。因为让路给了 ShadowDOM 体系。</p>
<h4 id="基于代理-Proxy-实现多实例沙箱"><a href="#基于代理-Proxy-实现多实例沙箱" class="headerlink" title="基于代理(Proxy)实现多实例沙箱"></a>基于代理(Proxy)实现多实例沙箱</h4><p>在单实例的场景总，我们的fakeWindow是一个空的对象，其没有任何储存变量的功能，微应用创建的变量最终实际都是挂载在window上的，这就限制了同一时刻不能有两个激活的微应用。</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><code class="hljs $xslt">class MultipleProxySandbox &#123;<br>    active() &#123;<br>        this.sandboxRunning = true<br>    &#125;<br>    inactive() &#123;<br>        this.sandboxRunning = false;<br>    &#125;<br>    <br>    /**<br>    * 构造函数<br>    * @param &#123;*&#125; name 沙箱名称 <br>    * @param &#123;*&#125; context 共享的上下文<br>    * @returns <br>    */<br>    constructor(name, context = &#123;&#125;) &#123;<br>        this.name = name;<br>        this.proxy = null;<br>        const fakeWindow = Object.create(&#123;&#125;);<br>        const proxy = new Proxy(fakeWindow,&#123;<br>            set: (target, name, value) =&gt; &#123;<br>                if (this.sandboxRunning) &#123;<br>                    if (Object.keys(context).includes(name)) &#123;<br>                        context[name] = value;<br>                    &#125;<br>                    target[name] = value;<br>                &#125;<br>                <br>            &#125;,<br>            get: (target, name) =&gt; &#123;<br>                // 优先使用共享对象<br>                if (Object.keys(context).includes(name)) &#123;<br>                    return context[name];<br>                &#125;<br>                return target[name];<br>            &#125;<br>        &#125;)<br>        this.proxy = proxy;<br>    &#125;<br>&#125;<br><br>const context = &#123; document: window.document &#125;;<br>const newSandBox1 = new MultipleProxySandbox(&#x27;代理沙箱1&#x27;, context);<br>newSandBox1.active();<br>const proxyWindow1 = newSandBox1.proxy;<br><br>const newSandBox2 = new MultipleProxySandbox(&#x27;代理沙箱2&#x27;, context);<br>newSandBox2.active();<br>const proxyWindow2 = newSandBox2.proxy;<br><br>console.log(&#x27;共享对象是否相等&#x27;, window.document === proxyWindow1.document, window.document ===  proxyWindow2.document); // true true<br><br>proxyWindow1.a = &#x27;1&#x27;; // 设置代理1的值<br>proxyWindow2.a = &#x27;2&#x27;; // 设置代理2的值<br>window.a = &#x27;3&#x27;;  // 设置window的值<br>console.log(&#x27;打印输出的值&#x27;, proxyWindow1.a, proxyWindow2.a, window.a); //1, 2, 3<br><br>newSandBox1.inactive(); newSandBox2.inactive(); // 两个沙箱都失活<br><br>proxyWindow1.a = &#x27;4&#x27;; // 设置代理1的值<br>proxyWindow2.a = &#x27;4&#x27;; // 设置代理2的值<br>window.a = &#x27;4&#x27;;  // 设置window的值<br>console.log(&#x27;失活后打印输出的值&#x27;, proxyWindow1.a, proxyWindow2.a, window.a); // 1,2 4<br><br>newSandBox1.active(); newSandBox2.active(); // 再次激活<br><br>proxyWindow1.a = &#x27;4&#x27;; // 设置代理1的值<br>proxyWindow2.a = &#x27;4&#x27;; // 设置代理2的值<br>window.a = &#x27;4&#x27;;  // 设置window的值<br>console.log(&#x27;失活后打印输出的值&#x27;, proxyWindow1.a, proxyWindow2.a, window.a); // 4,4,4<br><br><br></code></pre></td></tr></table></figure> 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E5%89%8D%E7%AB%AF%E5%BB%BA%E8%AE%BE/" rel="tag">前端建设</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E5%BE%AE%E5%89%8D%E7%AB%AF/" rel="tag">微前端</a></li></ul>

    </footer>
  </div>

   
    
</article>

    
    <article
  id="post-icon-svg组件封装"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2022/02/25/icon-svg%E7%BB%84%E4%BB%B6%E5%B0%81%E8%A3%85/"
    >微前端</a> 
</h2>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/02/25/icon-svg%E7%BB%84%E4%BB%B6%E5%B0%81%E8%A3%85/" class="article-date">
  <time datetime="2022-02-24T16:00:00.000Z" itemprop="datePublished">2022-02-25</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/vue%E7%9B%B8%E5%85%B3/">vue相关</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h4 id="写在前面"><a href="#写在前面" class="headerlink" title="写在前面"></a>写在前面</h4><p>在开发项目的时候，很多场景需要用到小图标，一般会采取以下做法：<br>1、雪碧图，缺点：将所有图标集中在一张雪碧图中，修改或新增图标需整张雪碧图替换，难为维护，开发要靠图标定位，开发效率低<br>2、png小图标： 缺点：模糊、失真，占用体积大<br>3、iconfont：需引入infont的css，如果修改图标需要整个css替换，同事，图标引用不具有语义化。<br>于是 svg 登场了……………….<br>svg的有点<br>*支持多色图标了，不再受单色限制。<br>*支持像字体那样通过font-size,color来调整样式。<br>*支持 ie9+<br>*可利用CSS实现动画。<br>*减少HTTP请求。<br>*矢量，缩放不失真<br>*可以很精细的控制SVG图标的每一部分</p>
<h4 id="封装一个全局组icon-svg组件"><a href="#封装一个全局组icon-svg组件" class="headerlink" title="封装一个全局组icon-svg组件"></a>封装一个全局组icon-svg组件</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">&lt;template&gt;<br>    &lt;svg<br>        :class=&quot;svgClass&quot;<br>        aria-hidden=&quot;true&quot;<br>        v-on=&quot;$listeners&quot;<br>        :style=&quot;&#123;width: width, height: height&#125;&quot;<br>    &gt;<br>        &lt;use :href=&quot;iconName&quot; /&gt;<br>    &lt;/svg&gt;<br>&lt;/template&gt;<br><br>&lt;script&gt;<br>    export default &#123;<br>        name: &#x27;IconSvg&#x27;,<br>        props: &#123;<br>            iconClass: &#123;<br>                type: String,<br>                required: true<br>            &#125;,<br>            className: &#123;<br>                type: String,<br>                default: &#x27;&#x27;<br>            &#125;,<br>            width: &#123;<br>                type: String,<br>                default: &#x27;16px&#x27;<br>            &#125;,<br>            height: &#123;<br>                type: String,<br>                default: &#x27;16px&#x27;<br>            &#125;<br>        &#125;,<br>        computed: &#123;<br>            iconName() &#123;<br>                return `#icon-$&#123;this.iconClass&#125;`<br>            &#125;,<br>            svgClass() &#123;<br>                if (this.className) &#123;<br>                    return &#x27;svg-icon &#x27; + this.className<br>                &#125; else &#123;<br>                    return &#x27;svg-icon&#x27;<br>                &#125;<br>            &#125;,<br>            styleExternalIcon() &#123;<br>                return &#123;<br>                    mask: `url($&#123;this.iconClass&#125;) no-repeat 50% 50%`,<br>                    &#x27;-webkit-mask&#x27;: `url($&#123;this.iconClass&#125;) no-repeat 50% 50%`<br>                &#125;<br>            &#125;<br>        &#125;<br>    &#125;<br>&lt;/script&gt;<br><br>&lt;style scoped&gt;<br>    .svg-icon &#123;<br>        width: 1em;<br>        height: 1em;<br>        vertical-align: -0.15em;<br>        fill: currentColor;<br>        overflow: hidden;<br>    &#125;<br><br>    .svg-external-icon &#123;<br>        background-color: currentColor;<br>        mask-size: cover!important;<br>        display: inline-block;<br>    &#125;<br>&lt;/style&gt;<br></code></pre></td></tr></table></figure>

<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs $xslt">//引入svg组件<br>import IconSvg from &#x27;@/components/IconSvg&#x27;<br><br>//全局注册icon-svg<br>Vue.component(&#x27;icon-svg&#x27;, IconSvg)<br><br>//在代码中使用<br>&lt;icon-svg icon-class=&quot;password&quot; /&gt;<br></code></pre></td></tr></table></figure>

<h5 id="引入所有的svg图标（使用svg-sprite）"><a href="#引入所有的svg图标（使用svg-sprite）" class="headerlink" title="引入所有的svg图标（使用svg-sprite）"></a>引入所有的svg图标（使用svg-sprite）</h5><p>制作 svg-sprite，要使用到 svg-sprite-loader 这个神器，它是一个 webpack loader ，可以将多个 svg 打包成 svg-sprite 。<br>如何在 vue-cli 的基础上进行改造，加入 svg-sprite-loader。先installsvg-sprite-loader，然后在vue.config.js中加入如下配置</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">chainWebpack(config) &#123;<br>    // set svg-sprite-loader<br>    config.module<br>        .rule(&#x27;svg&#x27;)<br>        .exclude.add(resolve(&#x27;src/common/components/icon-svg&#x27;))<br>        .end()<br>    config.module<br>        .rule(&#x27;icons&#x27;)<br>        .test(/\.svg$/)<br>        .include.add(resolve(&#x27;src/common/components/icon-svg&#x27;))<br>        .end()<br>        .use(&#x27;svg-sprite-loader&#x27;)<br>        .loader(&#x27;svg-sprite-loader&#x27;)<br>        .options(&#123;<br>          symbolId: &#x27;icon-[name]&#x27;<br>        &#125;)<br>        .end()<br><br>    // set preserveWhitespace<br>    config.module<br>        .rule(&#x27;vue&#x27;)<br>        .use(&#x27;vue-loader&#x27;)<br>        .loader(&#x27;vue-loader&#x27;)<br>        .tap(options =&gt; &#123;<br>          options.compilerOptions.preserveWhitespace = true<br>          return options<br>        &#125;)<br>        .end()<br>  &#125;<br></code></pre></td></tr></table></figure>
<p>这样配置好了，只要引入svg之后填写类名就可以了</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">import &#x27;@/src/icons/qq.svg; //引入图标<br><br>&lt;svg&gt;&lt;use xlink:href=&quot;#qq&quot; /&gt;&lt;/svg&gt;  //使用图标<br></code></pre></td></tr></table></figure>

<p>想到一个个import图标非常不优雅，且显得非常低级，所以要用到webpack 的 require.context。</p>
<ul>
<li>require.context(“./test”, false, /.test.js$/);这行代码就会去 test 文件夹（不包含子目录）下面的找所有文件名以 .test.js 结尾的文件能被 require 的文件。更直白的说就是 我们可以通过正则匹配引入相应的文件模块。</li>
</ul>
<p>然后我们就可以这样写来自动引入 @/src/icons 下面所有的图标了</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">const requireAll = requireContext =&gt; requireContext.keys().map(requireContext)<br>const req = require.context(&#x27;./svg&#x27;, false, /\.svg$/)<br>requireAll(req)<br></code></pre></td></tr></table></figure>

<h4 id="小小福利：欢迎关注【易惠之家】公众号，会不定时派送福利，包括全国寄快递半价，外卖10元优惠等等。"><a href="#小小福利：欢迎关注【易惠之家】公众号，会不定时派送福利，包括全国寄快递半价，外卖10元优惠等等。" class="headerlink" title="小小福利：欢迎关注【易惠之家】公众号，会不定时派送福利，包括全国寄快递半价，外卖10元优惠等等。"></a>小小福利：欢迎关注【易惠之家】公众号，会不定时派送福利，包括全国寄快递半价，外卖10元优惠等等。</h4><h4 id="作为一名打工仔，我们绝不作搬运工，而应该自己创造价值-财富，欢迎关注【易惠之家】一起共勉"><a href="#作为一名打工仔，我们绝不作搬运工，而应该自己创造价值-财富，欢迎关注【易惠之家】一起共勉" class="headerlink" title="作为一名打工仔，我们绝不作搬运工，而应该自己创造价值/财富，欢迎关注【易惠之家】一起共勉"></a>作为一名打工仔，我们绝不作搬运工，而应该自己创造价值/财富，欢迎关注【易惠之家】一起共勉</h4> 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/vue%E7%9B%B8%E5%85%B3/" rel="tag">vue相关</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E7%BB%84%E4%BB%B6%E5%B0%81%E8%A3%85/" rel="tag">组件封装</a></li></ul>

    </footer>
  </div>

   
    
</article>

    
    <article
  id="post-v-for_key"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2022/02/25/v-for_key/"
    >v-for中为何要添加唯一key</a> 
</h2>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/02/25/v-for_key/" class="article-date">
  <time datetime="2022-02-24T16:00:00.000Z" itemprop="datePublished">2022-02-25</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/vue%E7%9B%B8%E5%85%B3/">vue相关</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h4 id="v-for中的key"><a href="#v-for中的key" class="headerlink" title="v-for中的key"></a>v-for中的key</h4><p>使用v-for渲染列表时，默认采用就地复用策略，vue的底层代码会根据key值去对比新旧的虚拟dom，如果dom没有被修改，则复用。如果有被修改，则修改。这样有助于提升渲染性能。默认情况下v-for会将index设为默认key。但不推荐这样做。</p>
<h4 id="比较不同情况下采用-index-作为key带来的影响"><a href="#比较不同情况下采用-index-作为key带来的影响" class="headerlink" title="比较不同情况下采用 index 作为key带来的影响"></a>比较不同情况下采用 index 作为key带来的影响</h4><p>先定义一部分列表数据</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">const list = [<br>    &#123;<br>        id: 1,<br>        name: &#x27;test1&#x27;,<br>    &#125;,<br>    &#123;<br>        id: 2,<br>        name: &#x27;test2&#x27;,<br>    &#125;,<br>    &#123;<br>        id: 3,<br>        name: &#x27;test3&#x27;,<br>    &#125;,<br>]<br>const list = [<br>    &#123;<br>        id: 1,<br>        name: &#x27;test1&#x27;,<br>    &#125;,<br>    &#123;<br>        id: 2,<br>        name: &#x27;test2&#x27;,<br>    &#125;,<br>    &#123;<br>        id: 3,<br>        name: &#x27;test3&#x27;,<br>    &#125;,<br>]<br></code></pre></td></tr></table></figure>
<p>1.在最后一条数据后再加一条数据</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">const list = [<br>    &#123;<br>        id: 1,<br>        name: &#x27;test1&#x27;,<br>    &#125;,<br>    &#123;<br>        id: 2,<br>        name: &#x27;test2&#x27;,<br>    &#125;,<br>    &#123;<br>        id: 3,<br>        name: &#x27;test3&#x27;,<br>    &#125;,<br>    &#123;<br>        id: 4,<br>        name: &#x27;我是在最后添加的一条数据&#x27;,<br>    &#125;,<br>]<br></code></pre></td></tr></table></figure>
<p>这种情况下，前三条数据的key没有变，依旧分别是0，1，2，这样，前三条数据就会复用之前的，新渲染最后新添加的数据，木有问题。</p>
<p>2.在中间插入一条数据</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">const list = [<br>    &#123;<br>        id: 1,<br>        name: &#x27;test1&#x27;,<br>    &#125;,<br>    &#123;<br>        id: 4,<br>        name: &#x27;我是插队的那条数据&#x27;,<br>    &#125;<br>    &#123;<br>        id: 2,<br>        name: &#x27;test2&#x27;,<br>    &#125;,<br>    &#123;<br>        id: 3,<br>        name: &#x27;test3&#x27;,<br>    &#125;,<br>]<br></code></pre></td></tr></table></figure>
<p>此时更新渲染数据,通过index定义的key去进行前后数据的对比,发现</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">之前的数据                         之后的数据<br><br>key: 0  index: 0 name: test1     key: 0  index: 0 name: test1<br>key: 1  index: 1 name: test2     key: 1  index: 1 name: 我是插队的那条数据<br>key: 2  index: 2 name: test3     key: 2  index: 2 name: test2<br>                                 key: 3  index: 3 name: test3<br></code></pre></td></tr></table></figure>
<p>通过对比，可以清晰地发现，除了第一条数据的key还保留为原来的数据，其他的key都变了，所以只有第一条数据可以复用。<br>这不仅仅是导致渲染性能的问题，而且在某些情况下会导致渲染的问题，例如当列表项里有输入框的情况下，会导致输入框里的渲染值错位。</p>
<h5 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h5><p>综上所述，最好的办法是使用数组中不会变化的那一项作为key值,对应到项目中,即每条数据都有一个唯一的id,来标识这条数据的唯一性;使用id作为key值,我们再来对比一下向中间插入一条数据,此时会怎么去渲染</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">之前的数据                              之后的数据<br><br>key: 1  id: 1 index: 0 name: test1     key: 1  id: 1 index: 0  name: test1<br>key: 2  id: 2 index: 1 name: test2     key: 4  id: 4 index: 1  name: 我是插队的那条数据<br>key: 3  id: 3 index: 2 name: test3     key: 2  id: 2 index: 2  name: test2<br>                                       key: 3  id: 3 index: 3  name: test3<br></code></pre></td></tr></table></figure>
<p>现在对比发现只有一条数据变化了,就是id为4的那条数据,因此只要新渲染这一条数据就可以了,其他都是就复用之前的;</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/vue%E7%9B%B8%E5%85%B3/" rel="tag">vue相关</a></li></ul>

    </footer>
  </div>

   
    
</article>

    
    <article
  id="post-bushu"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2022/02/21/bushu/"
    >前端部署</a> 
</h2>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/02/21/bushu/" class="article-date">
  <time datetime="2022-02-20T16:00:00.000Z" itemprop="datePublished">2022-02-21</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/%E5%89%8D%E7%AB%AF%E5%BB%BA%E8%AE%BE/">前端建设</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h3 id="手动部署"><a href="#手动部署" class="headerlink" title="手动部署"></a>手动部署</h3><p>要经历一下步骤</p>
<ul>
<li>run build项目</li>
<li>将项目压缩</li>
<li>登录服务器，手动上传压缩包，解压</li>
</ul>
<p>手动部署工作繁琐重复，所以可以借助一些工具，自动化一键部署</p>
<h3 id="deploy-cli-service"><a href="#deploy-cli-service" class="headerlink" title="deploy-cli-service"></a>deploy-cli-service</h3><p>前端一键自动化部署脚手架服务，支持开发、测试、生产多环境配置。配置好后一键可完成部署。</p>
<h4 id="使用步骤"><a href="#使用步骤" class="headerlink" title="使用步骤"></a>使用步骤</h4><h5 id="1-安装-deploy-cli-service"><a href="#1-安装-deploy-cli-service" class="headerlink" title="1.安装 deploy-cli-service"></a>1.安装 deploy-cli-service</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">npm i deploy-cli-service -g // 全局安装<br>npm i deploy-cli-service --save-dev // 本地安装<br></code></pre></td></tr></table></figure>

<h5 id="2-初始化配置文件-在项目目录下"><a href="#2-初始化配置文件-在项目目录下" class="headerlink" title="2.初始化配置文件(在项目目录下)"></a>2.初始化配置文件(在项目目录下)</h5><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">deploy-cli-service init<br></code></pre></td></tr></table></figure>
<p>根据提示填写内容，会在项目根目录下生成deploy.config.js文件，初始化配置只会生成 dev (开发环境)、test (测试环境)、prod (生产环境) 三个配置，再有其他配置可参考模板自行配置。<br><img src="/images/deploy-cli-service.png"></p>
<h5 id="3-手动创建配置文件"><a href="#3-手动创建配置文件" class="headerlink" title="3.手动创建配置文件"></a>3.手动创建配置文件</h5><p>不使用上面的初始化命令的可以手动创建配置文件。在项目根目录下手动创建 deploy.config.js 文件，内容同init生成的内容，做相应修改即可</p>
<h5 id="4-部署"><a href="#4-部署" class="headerlink" title="4.部署"></a>4.部署</h5><p>注意：命令后面需要加 –mode 环境对象 (如：「–mode dev」)</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">deploy-cli-service deploy --mode dev<br></code></pre></td></tr></table></figure>

<p>输入Y或按下enter即可开始自动部署</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E5%89%8D%E7%AB%AF%E5%BB%BA%E8%AE%BE/" rel="tag">前端建设</a></li><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E5%89%8D%E7%AB%AF%E9%83%A8%E7%BD%B2/" rel="tag">前端部署</a></li></ul>

    </footer>
  </div>

   
    
</article>

    
    <article
  id="post-EventLoop"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2022/02/18/EventLoop/"
    >EventLoop</a> 
</h2>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/02/18/EventLoop/" class="article-date">
  <time datetime="2022-02-17T16:00:00.000Z" itemprop="datePublished">2022-02-18</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/%E5%89%8D%E7%AB%AF%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/">前端核心概念</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h4 id="javascript"><a href="#javascript" class="headerlink" title="javascript"></a>javascript</h4><p>javascript是一门单线程语言，在最新的HTML5中提出了Web-Worker，但JavaScript是单线程这一核心仍未改变。所以一切JavaScript版的多线程都是单线程模拟来的。</p>
<h4 id="JavaScript事件循环机制"><a href="#JavaScript事件循环机制" class="headerlink" title="JavaScript事件循环机制"></a>JavaScript事件循环机制</h4><p>既然js是单线程，就好比银行办理业务的窗口，只有一个人办理完后才能轮到下一个人，js任务也要一个个按顺序执行。如果一个任务耗时过长，那么下一个任务就必须等着。但是比如我想浏览新闻，图片返回很忙，难道也必须等着？于是，聪明的coder们想到了同步任务和异步任务。</p>
<ul>
<li>同步任务</li>
<li>异步任务<br>流程图如下：<br><img src="/images/eventLoop1.png"><br>1、检测是同步任务还是异步任务，同步任务会进入主线程执行，异步任务进入Event Table并注册函数，然后将其放入异步任务队列中等待执行。<br>2、主线程的任务执行完毕后，会去读取任务队列中的函数，进入主线程执行<br>3、上述过程会不断循环，就是eventLoop。</li>
</ul>
<p>举个栗子：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs $xslt">let data = [];<br>$.ajax(&#123;<br>    url:www.javascript.com,<br>    data:data,<br>    success:() =&gt; &#123;<br>        console.log(&#x27;发送成功!&#x27;);<br>    &#125;<br>&#125;)<br>console.log(&#x27;代码执行结束&#x27;);<br></code></pre></td></tr></table></figure>
<ul>
<li>ajax进入Event Table，注册回调函数success</li>
<li>执行console.log(‘代码执行结束’)。</li>
<li>ajax事件完成，回调函数success进入Event Queue。</li>
<li>主线程从Event Queue读取回调函数success并执行。</li>
</ul>
<h4 id="宏任务与微任务"><a href="#宏任务与微任务" class="headerlink" title="宏任务与微任务"></a>宏任务与微任务</h4><p>宏任务：</p>
<ul>
<li>script</li>
<li>setTimeout</li>
<li>setInterval</li>
<li>UI交互事件</li>
<li>postMessage</li>
</ul>
<p>微任务：</p>
<ul>
<li>promise.then</li>
<li>process.nextTick</li>
<li>mutationObserver</li>
</ul>
<h4 id="运行机制"><a href="#运行机制" class="headerlink" title="运行机制"></a>运行机制</h4><p>执行一个宏任务（栈中没有就从事件队列中获取）<br>执行过程中如果遇到微任务，就将他添加到微任务的任务队列中<br>宏任务执行完毕后，立即执行当前微任务队列中的任务<br>当前宏任务执行完毕，开始检查渲染，然后gui线程接管渲染<br>渲染完毕后，js线程继续接管，开始下一个宏任务（从事件队列中获取）</p>
<p>如图所示：</p>
<p><img src="/images/eventLoop2.png"></p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E5%89%8D%E7%AB%AF%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/" rel="tag">前端核心概念</a></li></ul>

    </footer>
  </div>

   
    
</article>

    
    <article
  id="post-微信登录"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2022/02/17/%E5%BE%AE%E4%BF%A1%E7%99%BB%E5%BD%95/"
    >微信登录</a> 
</h2>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/02/17/%E5%BE%AE%E4%BF%A1%E7%99%BB%E5%BD%95/" class="article-date">
  <time datetime="2022-02-16T16:00:00.000Z" itemprop="datePublished">2022-02-17</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91/">微信开发</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h3 id="公众号网页跳转页面授权登录"><a href="#公众号网页跳转页面授权登录" class="headerlink" title="公众号网页跳转页面授权登录"></a>公众号网页跳转页面授权登录</h3><p>1.进入页面获取token，检测登录态<br>2.检测到未登录，则跳转微信授权页<br>3.用户点击授权后，回跳到自己的网页<br>4.获取链接上的code(微信会将code拼在链接上返回)，传给后端进行登录<br>&emsp;&emsp;4.1 通过code换取网页授权access_token(后端工作)<br>&emsp;&emsp;4.2 拉取用户信息(需scope为 snsapi_userinfo)</p>
<h3 id="代码示例"><a href="#代码示例" class="headerlink" title="代码示例"></a>代码示例</h3><p>1.进入页面获取token，检测登录态</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs $xslt">api.getUserInfo()<br>// 检测接口返回的状态码，如果为401（跟后端商量好未登录返回的参数），则未登录<br>export function checkStatus(status, msg) &#123;<br>    switch (status) &#123;<br>        case 400:<br>            showToast(msg)<br>            break<br>        case 401:<br>            getAuthCode()<br>            break<br>        case 405:<br>            showToast(&#x27;网络请求错误,请求方法未允许!&#x27;)<br>            break<br>        case 500:<br>            showToast(&#x27;服务器错误,请联系管理员!&#x27;);<br>            break;<br>        default:<br>            showToast(msg)<br>    &#125;<br>&#125;<br>// getAuthCode即为跳转去微信授权页获取code进行后续登录操作，下一步骤将有示例<br></code></pre></td></tr></table></figure>

<h3 id="跳转微信授权页获取code"><a href="#跳转微信授权页获取code" class="headerlink" title="跳转微信授权页获取code"></a>跳转微信授权页获取code</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs $xslt">import yh_config from &quot;./yh_config/yh_config.js&quot;<br><br>/**<br>*appid: 公众号appid<br>*redirect_uri：用户授权后回跳的地址<br>*其余参数一般不用修改，详细可参考微信开发文档<br>*/<br>const getAuthCode = callback =&gt; &#123;<br>    // #ifdef H5<br>    let redirect_uri = encodeURIComponent(yh_config.homePageUrl)<br>    window.location.href = `https://open.weixin.qq.com/connect/oauth2/authorize?appid=$&#123;yh_config.appId&#125;&amp;redirect_uri=$&#123;yh_config.homePageUrl&#125;&amp;response_type=code&amp;scope=snsapi_userinfo&amp;state=1#wechat_redirect`<br>    // #endif<br>&#125;<br></code></pre></td></tr></table></figure>

<h3 id="获取回跳地址上的code进行登录"><a href="#获取回跳地址上的code进行登录" class="headerlink" title="获取回跳地址上的code进行登录"></a>获取回跳地址上的code进行登录</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs $xslt">async init() &#123;<br>    //loginState是为了记录登录态，防止下次进来重新用code去请求，因为code只能用一次<br>	let loginState = getStorage(&#x27;loginState&#x27;)<br>	let code  = getUrlParam(&#x27;code&#x27;)<br>       console.log(loginState, code)<br>       if ((loginState==0 || loginState==undefined) &amp;&amp; code) &#123;<br>           await this.loginByCode(code)<br>       &#125;<br>       await this.getUserInfo()<br>     &#125;,<br></code></pre></td></tr></table></figure> 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E5%BE%AE%E4%BF%A1%E5%BC%80%E5%8F%91/" rel="tag">微信开发</a></li></ul>

    </footer>
  </div>

   
    
</article>

    
    <article
  id="post-http和https"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2022/02/17/http%E5%92%8Chttps/"
    >http和https</a> 
</h2>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/02/17/http%E5%92%8Chttps/" class="article-date">
  <time datetime="2022-02-16T16:00:00.000Z" itemprop="datePublished">2022-02-17</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/%E5%89%8D%E7%AB%AF%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/">前端核心概念</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h3 id="http和https的基本概念"><a href="#http和https的基本概念" class="headerlink" title="http和https的基本概念"></a>http和https的基本概念</h3><p>HTTP: 是互联网上应用最为广泛的一种网络协议，是一个客户端和服务器请求和应答的标准（TCP），用于从www服务器传输超文本到本地浏览器的传输协议。<br>HTTPS: 是以安全为目标的http传输渠道，通俗得讲就是http得安全版。在http下加入ssl层，依靠证书验证服务器身份，并为通信内容加密。</p>
<h3 id="http与https的区别"><a href="#http与https的区别" class="headerlink" title="http与https的区别"></a>http与https的区别</h3><p>1、http协议传输的数据都是明文的，未经过加密，因此，用http传输敏感信息会非常不安全<br>2、https是在http的基础上加入了ssl协议，用于对传输数据进行加密，同时验证服务器的身份，确保数据传输的安全性。<br>3、端口不同，http的端口是80，而https的端口是443</p>
<h3 id="https的工作原理"><a href="#https的工作原理" class="headerlink" title="https的工作原理"></a>https的工作原理</h3><h5 id="1、客户端发起https请求"><a href="#1、客户端发起https请求" class="headerlink" title="1、客户端发起https请求"></a>1、客户端发起https请求</h5><p> 用户在浏览器上请求https网址，然后链接到服务器的443端口</p>
<h5 id="2、服务端的配置"><a href="#2、服务端的配置" class="headerlink" title="2、服务端的配置"></a>2、服务端的配置</h5><p>采用https协议的服务器必须要有一套数字证书，可以自己制作，也可以向组织申请。区别是自己颁发的证书需要通过客户端的验证通过才可以返问，而受信任公司申请的证书则不会弹出提示页面。</p>
<p>这套证书其实就是一对公钥私钥，如果对公钥和私钥不太理解，可以想象成一把钥匙和一个锁头，只是全世界只有你一个人有这把钥匙，你可以把锁头给别人，别人可以用这个锁把重要的东西锁起来，然后发给你，因为只有你一个人有这把钥匙，所以只有你才能看到被这把锁锁起来的东西。</p>
<h5 id="3、传送证书"><a href="#3、传送证书" class="headerlink" title="3、传送证书"></a>3、传送证书</h5><p>这个证书其实就是公钥，只是包含了很多信息，如证书的颁发机构、过期时间等等。</p>
<h5 id="4、客户端解析证书"><a href="#4、客户端解析证书" class="headerlink" title="4、客户端解析证书"></a>4、客户端解析证书</h5><p>客户端首先会验证公钥是否有效，比如颁发机构，过期时间等等，如果无效，则会警告提示</p>
<p>如果证书没有问题，那么就会生成一个随机值，然后用证书对改随机值进行加密。就好像上面说的，把随机值用锁头锁起来，这样除非有钥匙，不然看不到被锁住的内容。</p>
<h5 id="5、传送加密信息"><a href="#5、传送加密信息" class="headerlink" title="5、传送加密信息"></a>5、传送加密信息</h5><p>这部分传输的是用证书加密后的随机值，目的就是让服务端得到这个随机值，以后客户端和服务端的通信就可以通过这个随机值来进行加密解密了。</p>
<h5 id="6、服务端解密信息"><a href="#6、服务端解密信息" class="headerlink" title="6、服务端解密信息"></a>6、服务端解密信息</h5><p>服务端用私钥解密后，得到了客户端传过来的随机值(私钥)，然后把内容通过该值进行对称加密，所谓对称加密就是，将信息和私钥通过某种算法混合在一起，这样除非知道私钥，不然无法获取内容，而正好客户端和服务端都知道这个私钥，所以只要加密算法够彪悍，私钥够复杂，数据就够安全。</p>
<h5 id="7、传输加密后的信息"><a href="#7、传输加密后的信息" class="headerlink" title="7、传输加密后的信息"></a>7、传输加密后的信息</h5><p>这部分信息是服务段用私钥加密后的信息，可以在客户端被还原。</p>
<h5 id="8、客户端解密信息"><a href="#8、客户端解密信息" class="headerlink" title="8、客户端解密信息"></a>8、客户端解密信息</h5><p>客户端用之前生成的私钥解密服务段传过来的信息，于是获取了解密后的内容，整个过程第三方即使监听到了数据，也束手无策。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E5%89%8D%E7%AB%AF%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/" rel="tag">前端核心概念</a></li></ul>

    </footer>
  </div>

   
    
</article>

    
    <article
  id="post-内存泄露"
  class="article article-type-post"
  itemscope
  itemprop="blogPost"
  data-scroll-reveal
>
  <div class="article-inner">
    
    <header class="article-header">
       
<h2 itemprop="name">
  <a class="article-title" href="/2022/01/20/%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2/"
    >js内存泄露</a> 
</h2>
 

      
    </header>
     
    <div class="article-meta">
      <a href="/2022/01/20/%E5%86%85%E5%AD%98%E6%B3%84%E9%9C%B2/" class="article-date">
  <time datetime="2022-01-19T16:00:00.000Z" itemprop="datePublished">2022-01-20</time>
</a> 
  <div class="article-category">
    <a class="article-category-link" href="/categories/%E5%89%8D%E7%AB%AF%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/">前端核心概念</a>
  </div>
   
    </div>
      
    <div class="article-entry" itemprop="articleBody">
       
  <h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>每种语言都有它的内存管理机制，内存分配是一门语言的核心概念之一，JS的内存机制是JavaScript的基础概念之一。变量声明，对象的深复制和浅复制等都涉及到内存分配的概念，所以要学好JavaScript，必须了解js的内存机制。</p>
<h3 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h3><p>内存也是有生命周期的，一门语言的内存机制一般有如下生命周期：<br>1、内存分配。当我们声明变量、对象、函数等的时候，系统会自动为他们分配内存。<br>2、内存使用。及读写内存，即使用变量，调用函数的时候。<br>3、内存回收。变量、函数等使用完毕，会由垃圾回收机制对不再使用的变量进行回收。<br>即 内存分配 &gt; 内存使用 &gt; 内存回收<br>举个栗子：</p>
<figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs plaintext">var a = 20 // 在内存中给数值变量分配空间<br>alert(a + 20) // 使用内存<br>var a = null // 内存使用完毕后回收，内存泄露一般发生在这一步<br></code></pre></td></tr></table></figure>

<h3 id="什么是内存泄露"><a href="#什么是内存泄露" class="headerlink" title="什么是内存泄露"></a>什么是内存泄露</h3><p>对于持续运行的服务进程，必须及时释放不再用到的内存。否则内存占用越来越高，轻则影响系统运行，重则导致进程崩溃，没有用到的内存没有及时进行回收，就叫做内存泄露。</p>
<h3 id="JavaScript内存管理机制"><a href="#JavaScript内存管理机制" class="headerlink" title="JavaScript内存管理机制"></a>JavaScript内存管理机制</h3><h3 id="分配"><a href="#分配" class="headerlink" title="分配"></a>分配</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs $xslt">// 给数值变量分配内存<br>let number = 123<br>// 给字符串分配内存<br>const object = &#123;<br>    a: 1,<br>    b: null<br>&#125;<br>// 给数组及其包含的值分配内存(就像对象一样)<br>const array = [1, null, &#x27;abra&#x27;]<br>// 给函数(可调用的对象)分配内存<br>function func(a)&#123;<br>    return a<br>&#125;<br></code></pre></td></tr></table></figure>
<h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs $xslt">// 写入内存<br>number = 234<br>// 读取number 和 func 的内存，写入func 参数内存<br>func(number)<br></code></pre></td></tr></table></figure>

<h3 id="回收"><a href="#回收" class="headerlink" title="回收"></a>回收</h3><p>Javascript的内存回收机制能回收绝大部分的垃圾内存，但还是存在回收不了的情况，如果存在这些情况，需要开发者手动回收。</p>
 
      <!-- reward -->
      
    </div>
    

    <!-- copyright -->
    
    <footer class="article-footer">
       
  <ul class="article-tag-list" itemprop="keywords"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/tags/%E5%89%8D%E7%AB%AF%E6%A0%B8%E5%BF%83%E6%A6%82%E5%BF%B5/" rel="tag">前端核心概念</a></li></ul>

    </footer>
  </div>

   
    
</article>

    
  </article>
  

  
  <nav class="page-nav">
    
    <span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="extend next" rel="next" href="/page/2/">下一页</a>
  </nav>
  
</section>
</div>

      <footer class="footer">
  <div class="outer">
    <ul>
      <li>
        Copyrights &copy;
        2022
        <i class="ri-heart-fill heart_icon"></i> wenshijun
      </li>
    </ul>
    <ul>
      <li>
        
      </li>
    </ul>
    <ul>
      <li>
        
        
        <span>
  <span><i class="ri-user-3-fill"></i>访问人数:<span id="busuanzi_value_site_uv"></span></span>
  <span class="division">|</span>
  <span><i class="ri-eye-fill"></i>浏览次数:<span id="busuanzi_value_page_pv"></span></span>
</span>
        
      </li>
    </ul>
    <ul>
      
    </ul>
    <ul>
      
    </ul>
    <ul>
      <li>
        <!-- cnzz统计 -->
        
        <script type="text/javascript" src='https://s9.cnzz.com/z_stat.php?id=1278069914&amp;web_id=1278069914'></script>
        
      </li>
    </ul>
  </div>
</footer>    
    </main>
    <div class="float_btns">
      <div class="totop" id="totop">
  <i class="ri-arrow-up-line"></i>
</div>

<div class="todark" id="todark">
  <i class="ri-moon-line"></i>
</div>

    </div>
    <aside class="sidebar on">
      <button class="navbar-toggle"></button>
<nav class="navbar">
  
  <div class="logo">
    <a href="/"><img src="/images/logo.png" alt="努力小子-博客"></a>
  </div>
  
  <ul class="nav nav-main">
    
    <li class="nav-item">
      <a class="nav-item-link" href="/">主页</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/archives">归档</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/categories">分类</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/tags">标签</a>
    </li>
    
    <li class="nav-item">
      <a class="nav-item-link" href="/about/me">关于我</a>
    </li>
    
  </ul>
</nav>
<nav class="navbar navbar-bottom">
  <ul class="nav">
    <li class="nav-item">
      
      <a class="nav-item-link nav-item-search"  title="搜索">
        <i class="ri-search-line"></i>
      </a>
      
      
    </li>
  </ul>
</nav>
<div class="search-form-wrap">
  <div class="local-search local-search-plugin">
  <input type="search" id="local-search-input" class="local-search-input" placeholder="Search...">
  <div id="local-search-result" class="local-search-result"></div>
</div>
</div>
    </aside>
    <div id="mask"></div>

<!-- #reward -->
<div id="reward">
  <span class="close"><i class="ri-close-line"></i></span>
  <p class="reward-p"><i class="ri-cup-line"></i>请我喝碗白粥吧~</p>
  <div class="reward-box">
    
    <div class="reward-item">
      <img class="reward-img" src="/images/alipay.jpg">
      <span class="reward-type">支付宝</span>
    </div>
    
    
    <div class="reward-item">
      <img class="reward-img" src="/images/weixin.png">
      <span class="reward-type">微信</span>
    </div>
    
  </div>
</div>
    
<script src="/js/jquery-3.6.0.min.js"></script>
 
<script src="/js/lazyload.min.js"></script>

<!-- Tocbot -->

<script src="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.js"></script>
<link
  rel="stylesheet"
  href="https://cdn.jsdelivr.net/npm/jquery-modal@0.9.2/jquery.modal.min.css"
/>
<script src="https://cdn.jsdelivr.net/npm/justifiedGallery@3.7.0/dist/js/jquery.justifiedGallery.min.js"></script>

<script src="/dist/main.js"></script>

<!-- ImageViewer -->
 <!-- Root element of PhotoSwipe. Must have class pswp. -->
<div class="pswp" tabindex="-1" role="dialog" aria-hidden="true">

    <!-- Background of PhotoSwipe. 
         It's a separate element as animating opacity is faster than rgba(). -->
    <div class="pswp__bg"></div>

    <!-- Slides wrapper with overflow:hidden. -->
    <div class="pswp__scroll-wrap">

        <!-- Container that holds slides. 
            PhotoSwipe keeps only 3 of them in the DOM to save memory.
            Don't modify these 3 pswp__item elements, data is added later on. -->
        <div class="pswp__container">
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
            <div class="pswp__item"></div>
        </div>

        <!-- Default (PhotoSwipeUI_Default) interface on top of sliding area. Can be changed. -->
        <div class="pswp__ui pswp__ui--hidden">

            <div class="pswp__top-bar">

                <!--  Controls are self-explanatory. Order can be changed. -->

                <div class="pswp__counter"></div>

                <button class="pswp__button pswp__button--close" title="Close (Esc)"></button>

                <button class="pswp__button pswp__button--share" style="display:none" title="Share"></button>

                <button class="pswp__button pswp__button--fs" title="Toggle fullscreen"></button>

                <button class="pswp__button pswp__button--zoom" title="Zoom in/out"></button>

                <!-- Preloader demo http://codepen.io/dimsemenov/pen/yyBWoR -->
                <!-- element will get class pswp__preloader--active when preloader is running -->
                <div class="pswp__preloader">
                    <div class="pswp__preloader__icn">
                        <div class="pswp__preloader__cut">
                            <div class="pswp__preloader__donut"></div>
                        </div>
                    </div>
                </div>
            </div>

            <div class="pswp__share-modal pswp__share-modal--hidden pswp__single-tap">
                <div class="pswp__share-tooltip"></div>
            </div>

            <button class="pswp__button pswp__button--arrow--left" title="Previous (arrow left)">
            </button>

            <button class="pswp__button pswp__button--arrow--right" title="Next (arrow right)">
            </button>

            <div class="pswp__caption">
                <div class="pswp__caption__center"></div>
            </div>

        </div>

    </div>

</div>

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.css">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/default-skin/default-skin.min.css">
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/photoswipe@4.1.3/dist/photoswipe-ui-default.min.js"></script>

<script>
    function viewer_init() {
        let pswpElement = document.querySelectorAll('.pswp')[0];
        let $imgArr = document.querySelectorAll(('.article-entry img:not(.reward-img)'))

        $imgArr.forEach(($em, i) => {
            $em.onclick = () => {
                // slider展开状态
                // todo: 这样不好，后面改成状态
                if (document.querySelector('.left-col.show')) return
                let items = []
                $imgArr.forEach(($em2, i2) => {
                    let img = $em2.getAttribute('data-idx', i2)
                    let src = $em2.getAttribute('data-target') || $em2.getAttribute('src')
                    let title = $em2.getAttribute('alt')
                    // 获得原图尺寸
                    const image = new Image()
                    image.src = src
                    items.push({
                        src: src,
                        w: image.width || $em2.width,
                        h: image.height || $em2.height,
                        title: title
                    })
                })
                var gallery = new PhotoSwipe(pswpElement, PhotoSwipeUI_Default, items, {
                    index: parseInt(i)
                });
                gallery.init()
            }
        })
    }
    viewer_init()
</script> 
<!-- MathJax -->

<!-- Katex -->

<!-- busuanzi  -->
 
<script src="/js/busuanzi-2.3.pure.min.js"></script>
 
<!-- ClickLove -->
 
<script src="/js/clickLove.js"></script>
 
<!-- ClickBoom1 -->

<!-- ClickBoom2 -->

<!-- CodeCopy -->
 
<link rel="stylesheet" href="/css/clipboard.css">
 <script src="https://cdn.jsdelivr.net/npm/clipboard@2/dist/clipboard.min.js"></script>
<script>
  function wait(callback, seconds) {
    var timelag = null;
    timelag = window.setTimeout(callback, seconds);
  }
  !function (e, t, a) {
    var initCopyCode = function(){
      var copyHtml = '';
      copyHtml += '<button class="btn-copy" data-clipboard-snippet="">';
      copyHtml += '<i class="ri-file-copy-2-line"></i><span>COPY</span>';
      copyHtml += '</button>';
      $(".highlight .code pre").before(copyHtml);
      $(".article pre code").before(copyHtml);
      var clipboard = new ClipboardJS('.btn-copy', {
        target: function(trigger) {
          return trigger.nextElementSibling;
        }
      });
      clipboard.on('success', function(e) {
        let $btn = $(e.trigger);
        $btn.addClass('copied');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-checkbox-circle-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPIED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-checkbox-circle-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
      clipboard.on('error', function(e) {
        e.clearSelection();
        let $btn = $(e.trigger);
        $btn.addClass('copy-failed');
        let $icon = $($btn.find('i'));
        $icon.removeClass('ri-file-copy-2-line');
        $icon.addClass('ri-time-line');
        let $span = $($btn.find('span'));
        $span[0].innerText = 'COPY FAILED';
        
        wait(function () { // 等待两秒钟后恢复
          $icon.removeClass('ri-time-line');
          $icon.addClass('ri-file-copy-2-line');
          $span[0].innerText = 'COPY';
        }, 2000);
      });
    }
    initCopyCode();
  }(window, document);
</script>
 
<!-- CanvasBackground -->
 
<script src="/js/dz.js"></script>
 
<script>
  if (window.mermaid) {
    mermaid.initialize({ theme: "forest" });
  }
</script>


    
    

  </div>
</body>

</html>